/* -*-C-*-
 ##############################################################################
 #
 # File:        trice/src/analog.c
 # RCS:         "@(#)$Revision: 1.14 $ $Date: 94/03/09 10:19:58 $"
 # Description: User routines associated with analog input on a E1430 module.
 # Author:      Doug Passey
 # Created:     
 # Language:    C
 # Package:     E1430
 # Status:      "@(#)$State: Exp $"
 #
 # (C) Copyright 1992, Hewlett-Packard Company, all rights reserved.
 #
 ##############################################################################
 #
 # Please add additional comments here
 #
 # Revisions:
 #
 ##############################################################################
*/

#    include <stdio.h>

#include "trice.h"
#include "err1430.h"

#define MIN_RANGE 	0.0078125

#ifndef lint
const char analog_fileId[] = "$Header: analog.c,v 1.14 94/03/09 10:19:58 chriss Exp $";
#endif

static const SHORTSIZ16 rangeBits[RANGE_STEPS] = {	
		8,		/* 0.0078125 peak volts */
		9,		/* 0.015625 peak volts */
		10,		/* 0.03125 peak volts */
		0,		/* 0.0625 peak volts */
		1,		/* 0.125 peak volts */
		2,		/* 0.25 peak volts */
		3,		/* 0.50 peak volts */
		4,		/* 1.0 peak volts */
		5,		/* 2.0 peak volts */
		6,		/* 4.0 peak volts */
		7		/* 8.0 peak volts */
	};


/*****************************************************************************
 *
 * Returns in <bitsPtr> the register bits associated with setting the 
 * anti-alias filter to the state contained in <state>.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_alias_filter_bits(SHORTSIZ16 state, SHORTSIZ16 *bitsPtr)
{
  switch(state) {
    case E1430_ANTIALIAS_ON:
      *bitsPtr = ANALOG_SETUP_ANTIALIAS_ON;
      break;
    case E1430_ANTIALIAS_OFF:
      *bitsPtr = ANALOG_SETUP_ANTIALIAS_OFF;
      break;
    default:
      return (i1430_Error(ERR1430_ILLEGAL_ANTI_ALIAS_MODE, NULL, NULL));
  }

  return (0);
}


/*****************************************************************************
 *
 * Returns in <bitsPtr> the register bits associated with setting the 
 * range to the parm contained in <range>. It is set to a range greater than
 * or equal to <range>.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_range_bits(FLOATSIZ64 range, SHORTSIZ16 *bitsPtr)
{
  int i;
  FLOATSIZ64 stepRange = MIN_RANGE;	/* lowest range in volts */

  for(i=0; i < RANGE_STEPS; i++) {	/* step up if range > stepRange */
    if(stepRange >= range) break;
    stepRange *= 2.0;
  }

  if(i == RANGE_STEPS) { 	/* range greater than max range of 16.0 volts */
    i = RANGE_STEPS - 1;
  }

  *bitsPtr = rangeBits[i];	/* get range setting from array */
  
  return (0);
}


/*****************************************************************************
 *
 * Returns in <bitsPtr> the register bits associated with setting the 
 * range indicated in <index>, where <index> is an index into the 
 * rangeBits array.
 *
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_range_bits_index(SHORTSIZ16 index, SHORTSIZ16 *bitsPtr)
{
  if(index > RANGE_STEPS - 1) {
    return (-1);
  }

  *bitsPtr = rangeBits[index];	/* get range setting from array */
  
  return (0);
}


/*****************************************************************************
 *
 * Returns in <bitsPtr> the register bits associated with setting the 
 * input high source to the parm contained in <src>. 
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_input_hi_bits(SHORTSIZ16 src, SHORTSIZ16 *bitsPtr)
{
  switch(src) {
    case E1430_INPUT_HI_CONN:
      *bitsPtr = ANALOG_SETUP_INPUT_HI_CONN;
      break;
    case E1430_INPUT_HI_GROUND:
      *bitsPtr = ANALOG_SETUP_INPUT_HI_GROUND;
      break;
    default:
      return (i1430_Error(ERR1430_ILLEGAL_INPUT_SOURCE, NULL, NULL));
  }

  return (0);
}


/*****************************************************************************
 *
 * Returns in <bitsPtr> the register bits associated with setting the 
 * input low source to the parm contained in <src>. 
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_input_lo_bits(SHORTSIZ16 src, SHORTSIZ16 *bitsPtr)
{
  switch(src) {
    case E1430_INPUT_LO_FLOAT:
      *bitsPtr = ANALOG_SETUP_INPUT_LO_FLOAT;
      break;
    case E1430_INPUT_LO_GROUND:
      *bitsPtr = ANALOG_SETUP_INPUT_LO_GROUND;
      break;
    default:
      return (i1430_Error(ERR1430_ILLEGAL_INPUT_SOURCE, NULL, NULL));
  }

  return (0);
}


/*****************************************************************************
 *
 * Returns in <bitsPtr> the register bits associated with setting the 
 * coupling to the parm contained in <coupling>. 
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_coupling_bits(SHORTSIZ16 coupling, SHORTSIZ16 *bitsPtr)
{
  switch(coupling) {
    case E1430_COUPLING_AC:
      *bitsPtr = ANALOG_SETUP_COUPLING_AC;
      break;
    case E1430_COUPLING_DC:
      *bitsPtr = ANALOG_SETUP_COUPLING_DC;
      break;
    default:
      return (i1430_Error(ERR1430_ILLEGAL_COUPLING, NULL, NULL));
  }

  return (0);
}


/*****************************************************************************
 *
 * Set the DC offset to <offset>, where <offset> is interpreted as a signed
 * fractional part of the current range, i.e -1.0 <= <offset> <= 1.0.
 *
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_input_offset(SHORTSIZ16 la, FLOATSIZ64 offset)
{
  SHORTSIZ16 error;
  SHORTSIZ16 bits;

  if(offset > 1.0) offset = 1.0;
  if(offset < -1.0) offset = -1.0;

  bits = (SHORTSIZ16)(-offset * (FLOATSIZ64)(0x07FF)) + 0x800;

  error = e1430_write_register_image(la, E1430_INPUT_OFFSET_REG, bits);
  if(error) return(error);

  return (0); 
}


/*****************************************************************************
 *
 * Set input configuration.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_analog_input(SHORTSIZ16 groupID, FLOATSIZ64 range,
      SHORTSIZ16 coupling, SHORTSIZ16 filter, SHORTSIZ16 inHi, SHORTSIZ16 inLo)
{
  SHORTSIZ16 modSetupBits, bits;
  SHORTSIZ16 mask;
  SHORTSIZ16 error;

  /* get range bits */
  error = i1430_get_range_bits(range, &bits);
  if(error) return (error);
  modSetupBits = bits;

  /* get input high source bits */
  error = i1430_get_input_hi_bits(inHi, &bits);
  if(error) return (error);
  modSetupBits |= bits;

  /* get input low source bits */
  error = i1430_get_input_lo_bits(inLo, &bits);
  if(error) return (error);
  modSetupBits |= bits;

  /* set bits for coupling*/
  error = i1430_get_coupling_bits(coupling, &bits);
  if(error) return (error);
  modSetupBits |= bits;

  error = i1430_get_alias_filter_bits(filter, &bits);
  if(error) return (error);
  modSetupBits |= bits;

  /* set mask for all these bits */
  mask =  ANALOG_SETUP_RANGE_MASK & ANALOG_SETUP_INPUT_HI_MASK & 
	  ANALOG_SETUP_INPUT_LO_MASK & ANALOG_SETUP_ANTIALIAS_MASK 
	  & ANALOG_SETUP_COUPLING_MASK;

  return (i1430_update_group_module_bits(groupID, E1430_ANALOG_SETUP_REG, 
							mask, modSetupBits));
}


/*****************************************************************************
 *
 * Sets range from logical address.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_range_la(SHORTSIZ16 la, FLOATSIZ64 range)
{
  SHORTSIZ16 bits;
  SHORTSIZ16 index;
  SHORTSIZ16 reg;
  SHORTSIZ16 error;
  SHORTSIZ16 i, updateTrig;

  error = i1430_get_index_from_la(la, &index);
  if(error) return(error);

  /* get range bits */
  error = i1430_get_range_bits(range, &bits);
  if(error) return (error);

  error = e1430_read_register_image(la, E1430_ANALOG_SETUP_REG, &reg);
  if(error) return(error);

  /* if the inverting amplifier is going to be switched in or out with
   * this range change, need to update trigger slope and trigger level
   * for the adc triggering case
   */
  if((reg & 8) != (bits & 8)) {
    updateTrig = 1;
  }else{
    updateTrig = 0;
  }

  reg &= ANALOG_SETUP_RANGE_MASK;
  reg |= bits;

  error = e1430_write_register_image(la, E1430_ANALOG_SETUP_REG, reg);
  if(error) return(error);
  
  for(i=0; i<RANGE_STEPS; i++) {		/* get correct offset */
    if(rangeBits[i]==(e1430_modStates[index].analogSetup & 
						~ANALOG_SETUP_RANGE_MASK)){
      reg = e1430_modStates[index].offsetSave[i];
      break;
    }
  }
  error = e1430_write_register_image(la, E1430_INPUT_OFFSET_REG, reg);
  if(error) return(error); 

  return(error);

}


/*****************************************************************************
 *
 * Sets range.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_range(SHORTSIZ16 groupID, FLOATSIZ64 range)
{
  SHORTSIZ16 bits;
  SHORTSIZ16 error;

  /* get range bits */
  error = i1430_get_range_bits(range, &bits);
  if(error) return (error);
  return (i1430_update_group_module_bits(groupID, E1430_ANALOG_SETUP_REG, 
				 ANALOG_SETUP_RANGE_MASK, bits));
}


/*****************************************************************************
 *
 * Returns the range in peak volts of the module with <index> in
 * the e1430_modStates array.
 * If there are illegal range bits in the analog setup register, it 
 * return 0.0;
 *
 ****************************************************************************/
FLOATSIZ64 i1430_get_range_index(SHORTSIZ16 index)
{
  int i;
  SHORTSIZ16 bits;
  FLOATSIZ64 range = MIN_RANGE;

  bits = e1430_modStates[index].analogSetup & ~ANALOG_SETUP_RANGE_MASK;

  for(i=0; i < RANGE_STEPS; i++) {
    if(bits == rangeBits[i]) {
      return (range);
    }

    range *= 2.0;
  }

  return (0.0);		/* shouldn't get here */
}
  
/*****************************************************************************
 *
 * Returns the range in peak volts of the module group, <groupID>, into
 * the variable pointed to by <rangePtr>.  
 * Returns 0 if OK, returns ERR1430_PARAMETER_UNEQUAL if the range is
 * not the same for all modules in the group.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_range(SHORTSIZ16 groupID, FLOATSIZ64 *rangePtr)
{
  return (i1430_get_float_parm(groupID, i1430_get_range_index, "range", rangePtr));
}
 

/*****************************************************************************
 *
 * Returns the range in peak volts of the module at logical addr, <la>, into
 * the variable pointed to by <rangePtr>.  
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_range_la(SHORTSIZ16 la, FLOATSIZ64 *rangePtr)
{
  SHORTSIZ16 index;
  SHORTSIZ16 error;

  error = i1430_get_index_from_la(la, &index);
  if(error) return(error);

  *rangePtr = i1430_get_range_index(index);
  return (0);
}
 

/*****************************************************************************
 *
 * Returns the DC offset in volts of the module with <index> in
 * the e1430_modStates array.
 * If there are illegal range bits in the analog setup register, it 
 * return 0.0;
 *
 ****************************************************************************/
FLOATSIZ64 i1430_get_input_offset_index(SHORTSIZ16 index)
{
  SHORTSIZ16 offsetBits;

  offsetBits = e1430_modStates[index].offset;

  return(((FLOATSIZ64)0x800 - (FLOATSIZ64)offsetBits)/(FLOATSIZ64)0x7FF); 
  
}


/*****************************************************************************
 *
 * Returns the input offset of the module group, <groupID>, into
 * the variable pointed to by <offsetPtr>.  
 * Returns 0 if OK, returns ERR1430_PARAMETER_UNEQUAL if the offset is
 * not the same for all modules in the group.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_input_offset(SHORTSIZ16 la, FLOATSIZ64 *offsetPtr)
{
  SHORTSIZ16 error, index;

  error = i1430_get_index_from_la(la, &index);
  if(error) return(error);

  *offsetPtr  = i1430_get_input_offset_index(index);

  return (0);
}
 

/*****************************************************************************
 *
 * Returns the coupling of module with index <index> in the e1430_modStates array.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_coupling_index(SHORTSIZ16 index)
{
  switch(e1430_modStates[index].analogSetup & ~ANALOG_SETUP_COUPLING_MASK) {
    case ANALOG_SETUP_COUPLING_DC:
      return (E1430_COUPLING_DC);
    case ANALOG_SETUP_COUPLING_AC:
      return (E1430_COUPLING_AC);
    default:
      return (i1430_Error(ERR1430_ILLEGAL_COUPLING, NULL, NULL));
  }
}

/*****************************************************************************
 *
 * Returns the coupling of the module group, <groupID>, into
 * the variable pointed to by <couplingPtr>.  
 * Returns 0 if OK, returns ERR1430_PARAMETER_UNEQUAL if the coupling is
 * not the same for all modules in the group.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_coupling(SHORTSIZ16 groupID, SHORTSIZ16 *couplingPtr)
{
  return (i1430_get_short_parm(groupID, i1430_get_coupling_index, "coupling", couplingPtr));
}
 

/*****************************************************************************
 *
 * Returns the input high source of module with index <index> in the 
 * e1430_modStates array.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_input_hi_index(SHORTSIZ16 index)
{
  switch(e1430_modStates[index].analogSetup & ~ANALOG_SETUP_INPUT_HI_MASK) {
    case ANALOG_SETUP_INPUT_HI_CONN:
      return (E1430_INPUT_HI_CONN);
    case ANALOG_SETUP_INPUT_HI_GROUND:
      return (E1430_INPUT_HI_GROUND);
    default:
      return (i1430_Error(ERR1430_ILLEGAL_INPUT_SOURCE, NULL, NULL));
  }
}


/*****************************************************************************
 *
 * Returns the input high source of the module group, <groupID>, into
 * the variable pointed to by <sourcePtr>.  
 * Returns 0 if OK, returns ERR1430_PARAMETER_UNEQUAL if the source is
 * not the same for all modules in the group.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_input_high(SHORTSIZ16 groupID, SHORTSIZ16 *sourcePtr)
{
  return (i1430_get_short_parm(groupID, i1430_get_input_hi_index, "input high", 
							sourcePtr));
}
 

/*****************************************************************************
 *
 * Returns the input low source of module with index <index> in the 
 * e1430_modStates array.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_input_lo_index(SHORTSIZ16 index)
{
  switch(e1430_modStates[index].analogSetup & ~ANALOG_SETUP_INPUT_LO_MASK) {
    case ANALOG_SETUP_INPUT_LO_FLOAT:
      return (E1430_INPUT_LO_FLOAT);
    case ANALOG_SETUP_INPUT_LO_GROUND:
      return (E1430_INPUT_LO_GROUND);
    default:
      return (i1430_Error(ERR1430_ILLEGAL_INPUT_SOURCE, NULL, NULL));
  }
}


/*****************************************************************************
 *
 * Returns the input low source of the module group, <groupID>, into
 * the variable pointed to by <sourcePtr>.  
 * Returns 0 if OK, returns ERR1430_PARAMETER_UNEQUAL if the source is
 * not the same for all modules in the group.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_input_low(SHORTSIZ16 groupID, SHORTSIZ16 *sourcePtr)
{
  return (i1430_get_short_parm(groupID, i1430_get_input_lo_index, "input low", 
							sourcePtr));
}
 

/*****************************************************************************
 *
 * Returns the state of the antialias filter of module with index <index> 
 * in the e1430_modStates array.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_get_alias_filter_index(SHORTSIZ16 index)
{
  switch(e1430_modStates[index].analogSetup & ~ANALOG_SETUP_ANTIALIAS_MASK) {
    case ANALOG_SETUP_ANTIALIAS_ON:
      return (E1430_ANTIALIAS_ON);
    case ANALOG_SETUP_ANTIALIAS_OFF:
      return (E1430_ANTIALIAS_OFF);
    default:
      return (i1430_Error(ERR1430_ILLEGAL_ANTI_ALIAS_MODE, NULL, NULL));
  }
}


/*****************************************************************************
 *
 * Returns the anti alias filter state of the module group, <groupID>, into
 * the variable pointed to by <statePtr>.  
 * Returns 0 if OK, returns ERR1430_PARAMETER_UNEQUAL if the source is
 * not the same for all modules in the group.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_anti_alias_filter(SHORTSIZ16 groupID, SHORTSIZ16 *statePtr)
{ 
  return (i1430_get_short_parm(groupID, i1430_get_alias_filter_index, 
				"anti alias filter", statePtr));
}
 

/*****************************************************************************
 *
 * Sets coupling.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_coupling(SHORTSIZ16 groupID, SHORTSIZ16 coupling)
{
  SHORTSIZ16 bits;
  SHORTSIZ16 error;

  error = i1430_get_coupling_bits(coupling, &bits);
  if(error) return (error);

  return (i1430_update_group_module_bits(groupID, E1430_ANALOG_SETUP_REG, 
				 ANALOG_SETUP_COUPLING_MASK, bits));
}


/*****************************************************************************
 *
 * Sets anti alias filter state.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_anti_alias_filter(SHORTSIZ16 groupID, SHORTSIZ16 state)
{
  SHORTSIZ16 bits;
  SHORTSIZ16 error;

  error = i1430_get_alias_filter_bits(state, &bits);
  if(error) return (error);

  return (i1430_update_group_module_bits(groupID, E1430_ANALOG_SETUP_REG, 
				 ANALOG_SETUP_ANTIALIAS_MASK, bits));
}


/*****************************************************************************
 *
 * Sets input high connector.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_input_high(SHORTSIZ16 groupID, SHORTSIZ16 source)
{
  SHORTSIZ16 bits;
  SHORTSIZ16 error;

  error = i1430_get_input_hi_bits(source, &bits);
  if(error) return (error);

  return (i1430_update_group_module_bits(groupID, E1430_ANALOG_SETUP_REG, 
				 ANALOG_SETUP_INPUT_HI_MASK, bits));
}


/*****************************************************************************
 *
 * Sets input connector shield.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_input_low(SHORTSIZ16 groupID, SHORTSIZ16 source)
{
  SHORTSIZ16 bits;
  SHORTSIZ16 error;


  error = i1430_get_input_lo_bits(source, &bits);
  if(error) return (error);

  return (i1430_update_group_module_bits(groupID, E1430_ANALOG_SETUP_REG, 
				 ANALOG_SETUP_INPUT_LO_MASK, bits));
}


/*****************************************************************************
 *
 * Gets the scale factor to multiply raw data with to produce data in
 * volts .
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_scale_la(SHORTSIZ16 la, FLOATSIZ64 *scalePtr)
{
  SHORTSIZ16 error, reg;
  FLOATSIZ64 range;

  error = e1430_get_range_la(la, &range);
  if(error) return(error);

  error = e1430_read_register_image(la, E1430_ANALOG_SETUP_REG, &reg);
  if(error) return(error);
 
  if(reg & 0x0008) {	/* if inverting amplifier turned on, negate range */
    range = -range;
  }

  error = e1430_read_register_image(la, E1430_DATA_FORMAT_REG, &reg);
  if(error) return(error);

  if(reg & DATA_FORMAT_DATA_SIZE_32) {
    *scalePtr = range / ((FLOATSIZ64)0x80000000 * KESTREL_FACTOR);
  }else{
    *scalePtr = range / ((FLOATSIZ64)0x8000 * KESTREL_FACTOR);
  }

  return(0);
}


